package com.jindouyun.provider.handler;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jindouyun.common.annotations.JDYService;
import com.jindouyun.common.entity.JDYRequest;
import com.jindouyun.common.entity.JDYResponse;
import com.jindouyun.common.exception.JdyException;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @className: com.jindouyun.provider.handler.RpcServerHandler
 * @description: RPC服务端处理器
 * @author: tong.li
 * @createTime: 2021/10/13 11:54
 * @copyright: Tongcheng-Elong Holdings Limited
 */
@Component
// 设置为Channel共享
@ChannelHandler.Sharable
public class RpcServerHandler extends SimpleChannelInboundHandler<String> implements ApplicationContextAware {

    private static final   Logger logger = LoggerFactory.getLogger(RpcServerHandler.class);

    private static final Map<String, Object> SERVICE_MAP = new ConcurrentHashMap();

    @Resource
    private ObjectMapper objectMapper;



   /**
    * RPC处理器主要逻辑：
    *  1. 将标有@JDYService注解的bean对象进行缓存
    *  2. 接收客户端的请求
    *  3. 根据客户端传递的serviceName从缓存中对应的bean实例
    *  4. 通过反射调用对应的客户端传递的actionName方法
    *  5. 给客户端进行响应
    */

    /**
     * 注入ApplicationContext，缓存带有@JDYService注解的bean
     * @param applicationContext
     * @throws BeansException
     */
   @Override
   public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
       // 通过ApplicationContext获取带有@JDYService的所有bean对象
       Map<String, Object> serviceBeans = applicationContext.getBeansWithAnnotation(JDYService.class);
       for (Map.Entry<String, Object> entry : serviceBeans.entrySet()) {
           Object bean = entry.getValue();
           if (bean.getClass().getInterfaces().length == 0 ) {
               throw new JdyException(String.format("对外暴露的RPC服务类[%s]必须实现接口",bean.getClass()));
           }

           // 这里默认取第一个接口，后续可以优化
           String name = bean.getClass().getInterfaces()[0].getName();
           // 缓存当前bean
           SERVICE_MAP.put(name,bean);
           logger.info("当前项目的服务缓存字典为:" + SERVICE_MAP);
       }

   }

    /**
     * Channel就绪读取事件
     * @param ctx
     * @param body
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String body) throws JsonProcessingException {
        JDYRequest request = null;
        JDYResponse response = null;
        try {
            // 将客户端的请求参数转化为JDYRequest对象
            request = objectMapper.readValue(body, JDYRequest.class);
            response = JDYResponse.ok(request.getRequestId(),  execute(request));
        }catch (JdyException e) {
            logger.error(e.getMessage(),e);
            response = JDYResponse.fail(request == null ? null : request.getRequestId(),e.getMessage());
        }catch (Exception e) {
            logger.error("服务器错误",e);
            response = JDYResponse.fail(request == null ? null : request.getRequestId(),e.getMessage());
        }
        // 将结果响应给客户端
        ctx.writeAndFlush(objectMapper.writeValueAsString(response));
    }

    private Object execute(JDYRequest request) throws JdyException, InvocationTargetException, JsonProcessingException {
        String serviceName = request.getServiceName();
        Object serviceBean = SERVICE_MAP.get(serviceName);
        if (null == serviceBean) {
            throw new JdyException(String.format("未知的服务名[%s]",serviceName));
        }
        // 适配参数类型
        Object[] parameters = request.getParameters();
        Class<?>[] parameterTypes = request.getParameterTypes();
        boolean repeatSetParams = false;
        for (int i = 0; i < parameters.length; i++) {
            Object parameter = parameters[i];
            Class<?> parameterType = parameterTypes[i];
            if (parameter.getClass() != parameterType) {
                parameters[i] =  objectMapper.readValue(objectMapper.writeValueAsString(parameter),parameterType);
                repeatSetParams = true;
            }
        }
        // 重新设置请求参数
        if (repeatSetParams) {
            request.setParameters(parameters);
        }
        // 使用cjlib库的FastClass类创建代理类
        FastClass serviceProxy = FastClass.create(serviceBean.getClass());
        // 代理对象获取方法
        FastMethod method = serviceProxy.getMethod(request.getActionName(), parameterTypes);
        // 代理对象进行反射方法调用
        return method.invoke(serviceBean,parameters);
    }


}
